package nuist.hanniko.stu.webbackend.service.cmd_runner;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import nuist.hanniko.stu.webbackend.dao.mapper.LwM2mDeviceMapper;
import nuist.hanniko.stu.webbackend.dao.mapper.LwM2mObjectMapper;
import nuist.hanniko.stu.webbackend.dao.mapper.LwM2mResourceMapper;
import nuist.hanniko.stu.webbackend.model.LwM2m.LwM2mDevice;
import nuist.hanniko.stu.webbackend.model.LwM2m.LwM2mObject;
import nuist.hanniko.stu.webbackend.model.LwM2m.LwM2mResource;
import nuist.hanniko.stu.webbackend.util.RandomNameGen;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Component
@Order(value = 2)
public class DeviceRegistrationService implements CommandLineRunner{

    private static final Logger LOG = LoggerFactory.getLogger(DeviceRegistrationService.class);

    private static final String REG_CHANNEL = "LESHAN_REG_NEW";
    private static final String DEL_CHANNEL = "LESHAN_REG_DEL";
    private static final String MODEL_CHANNEL = "LESHAN_REG_MODEL";

    @Autowired
    private RedisMessageListenerContainer container;

    @Autowired
    private LwM2mDeviceMapper deviceMapper;

    @Autowired
    private LwM2mResourceMapper resourceMapper;

    @Autowired
    private LwM2mObjectMapper objectMapper;

    @Override
    public void run(String... args) throws Exception {
        LOG.info("-------- 注册事件监听器 - 启动 --------");

        // 新注册监听器
        container.addMessageListener(registerListener, new ChannelTopic(REG_CHANNEL));

        // 删除监听器
        container.addMessageListener(deleteListener, new ChannelTopic(DEL_CHANNEL));

        // model监听器
        container.addMessageListener(modelListener, new ChannelTopic(MODEL_CHANNEL));
    }

    private MessageListener registerListener = (message, bytes) -> {
        JSONObject jsonObject = (JSONObject) JSON.parse(message.toString());

        if (jsonObject.containsKey("regDate")) {
            // 注册事件
            LOG.info("endpoint-{} register", jsonObject.getString("ep"));
            JSONObject addAttr = jsonObject.getJSONObject("addAttr");
            LwM2mDevice device = new LwM2mDevice(
                    jsonObject.getString("ep"),
                    RandomNameGen.getRandomString(),
                    new Date(Long.parseLong(jsonObject.getString("regDate"))),
                    null,
                    "running",
                    (String) addAttr.getOrDefault("model", "unknown"));

            if (deviceMapper.getByDeviceId(jsonObject.getString("ep")) == null) {
                deviceMapper.insert(device);
            } else {
                deviceMapper.update(device);
            }
        }
    };

    private MessageListener deleteListener = (message, bytes) -> {
        JSONObject jsonObject = (JSONObject) JSON.parse(message.toString());
        LOG.info("endpoint-{} delete registration", jsonObject.getString("ep"));
        deviceMapper.updateStateById(jsonObject.getString("ep"), "stopped");
    };

    private MessageListener modelListener = ((message, bytes) -> {
        JSONObject data = JSON.parseObject(message.toString());

        String deviceId = data.getString("ep");
        JSONArray objects = data.getJSONArray("objs");

        List<LwM2mResource> resourceList = new ArrayList<>();
        List<LwM2mObject> objectList = new ArrayList<>();
        for (JSONObject object : objects.toJavaList(JSONObject.class)) {
            String objectId = object.getString("objectId");
            String name = object.getString("name");
            objectList.add(new LwM2mObject(deviceId, objectId, name));

            for (JSONObject resource : object.getJSONArray("resources").toJavaList(JSONObject.class)) {
                resourceList.add(new LwM2mResource(deviceId, objectId,
                        resource.getString("id"),
                        resource.getString("name"),
                        resource.getString("operations"),
                        resource.getString("type"))
                );
            }
        }

        LOG.info("[endpoint-{} model reg] obj cnt: {}", deviceId, objectList.size());
        LOG.info("[endpoint-{} model reg] res cnt: {}", deviceId, resourceList.size());

        objectMapper.insertBatch(objectList);
        resourceMapper.insertBatch(resourceList);
    });
}
